#include "Texture2D.hpp"
#include <Utility/Tools.hpp>
#include "../../FBO/FBO.hpp"

namespace zzz{
//you cannot gen texture in constructor
//since it might construct before opengl is initialized
Texture2D::Texture2D(int newiformat)
:Texture(GL_TEXTURE_2D, newiformat), width_(0), height_(0)
{}

Texture2D::Texture2D(zuint width, zuint height, int newiformat)
:Texture(GL_TEXTURE_2D, newiformat)
{
  Create(width,height);
}

Texture2D::Texture2D(const string &filename, int newiformat)
:Texture(GL_TEXTURE_2D, newiformat)
{
  Create(filename);
}

bool Texture2D::Bind(GLenum bindto/*=GL_TEXTURE0*/)
{
  CHECK_GL_ERROR()
  GLActiveTexture::Set(bindto);
  CHECK_GL_ERROR()
//   Disable(bindto);
  CHECK_GL_ERROR()
  glEnable(GL_TEXTURE_2D);
  CHECK_GL_ERROR()
  
  GLBindTexture2D::Set(GetID());
  CHECK_GL_ERROR()

  GLActiveTexture::Restore();
  CHECK_GL_ERROR()
  return true;
}

void Texture2D::Create(int newiformat)
{
  if (internal_format_==newiformat)
    return;
  internal_format_=newiformat;
  Create();
  CHECK_GL_ERROR()
}

void Texture2D::Create(zuint width, zuint height)
{
  if (width_==width || height == height_)
    return;
  static const int max_size = GetMaxSize();
  if (int(width) > max_size || int(height) > max_size) {
    ZLOGV<<"Cannot create texture, too large size: "
      <<width<<"x"<<height<<", max size is "<< max_size<<endl;
    return;
  }
  width_=width;
  height_=height;
  Create();
  CHECK_GL_ERROR()
}

void Texture2D::Unbind()
{
  GLBindTexture2D::Restore();
}

void Texture2D::Create(const string &filename)
{
  Image4uc img;
  img.LoadFile(filename);
  ImageToTexture(img);
  CHECK_GL_ERROR()
}

void Texture2D::Create()
{
  CHECK_GL_ERROR()
  Bind();
  CHECK_GL_ERROR()
  if (internal_format_==GL_DEPTH_COMPONENT)
    glTexImage2D(GL_TEXTURE_2D,0,internal_format_,width_,height_,0,GL_DEPTH_COMPONENT,GL_UNSIGNED_BYTE,NULL);
  else
    glTexImage2D(GL_TEXTURE_2D,0,internal_format_,width_,height_,0,GL_RGB,GL_UNSIGNED_BYTE,NULL);
  CHECK_GL_ERROR()
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_para_);
  CHECK_GL_ERROR()
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter_para_);
  CHECK_GL_ERROR()
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_para_);
  CHECK_GL_ERROR()
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_para_);
  CHECK_GL_ERROR()
}

void Texture2D::ChangeInternalFormat(int newiformat)
{
  if (internal_format_==newiformat) return;
  Image4f img;
  TextureToImage(img);
  internal_format_=newiformat;
  ImageToTexture(img);
  CHECK_GL_ERROR()
}

void Texture2D::ChangeSize(zuint width,zuint height)
{
  if (width==width_ && height==height_) return;
  width_=width;
  height_=height;
  Image4f img;
  TextureToImage(img);
  img.Resize(height_,width_);
  ImageToTexture(img);
  CHECK_GL_ERROR()
}

void Texture2D::ChangeParameter(GLenum filter,GLenum wrap)
{
  CHECK_GL_ERROR()
  Bind();
  CHECK_GL_ERROR()
  if (filter!=0)
  {
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
    CHECK_GL_ERROR()
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
    CHECK_GL_ERROR()
  }
  if (wrap!=0)
  {
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);
    CHECK_GL_ERROR()
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
    CHECK_GL_ERROR()
  }
  filter_para_=filter;
  wrap_para_=wrap;
  CHECK_GL_ERROR()
}

bool Texture2D::CopyTexture(Texture2D &from, Texture2D &to)
{
  CHECK_GL_ERROR()
  to.internal_format_=from.internal_format_;
  to.filter_para_=from.filter_para_;
  to.wrap_para_=from.wrap_para_;
  to.Create(from.width_,from.height_);

  FBO fbo;
  CHECK_GL_ERROR()
  fbo.AttachTexture(GL_TEXTURE_2D,to.GetID());
  CHECK_GL_ERROR()
  fbo.Bind();
  CHECK_GL_ERROR()

  from.Bind();
  CHECK_GL_ERROR()

  glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0,from.width_,from.height_);
  CHECK_GL_ERROR()

  from.DisableAll();
  CHECK_GL_ERROR()
  fbo.Disable();
  CHECK_GL_ERROR()
  fbo.UnattachAll();
  CHECK_GL_ERROR()

  return true;
}
}